﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Linq;

namespace Gmantis.Controls
{
    public enum TabPanelOverlapDirection
    {
        Left = 2,
        Right = 1,
        RightLeftFromSelected = 3
    }

    public class TabPanel : Panel
    {
        // Fields
        private int _measureStartRank;
        private int _startRank;
        public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(TabPanel), new PropertyMetadata(Orientation.Horizontal, new PropertyChangedCallback(TabPanel.OnOrientationPropertyChanged)));
        internal static readonly DependencyProperty OverlapDirectionProperty = DependencyProperty.Register("OverlapDirection", typeof(TabPanelOverlapDirection), typeof(TabPanel), new PropertyMetadata(TabPanelOverlapDirection.Left, new PropertyChangedCallback(TabPanel.OnOverlapDirectionPropertyChanged)));
        internal static readonly DependencyProperty OverlapProperty = DependencyProperty.Register("Overlap", typeof(int), typeof(TabPanel), new PropertyMetadata(new PropertyChangedCallback(TabPanel.OnOverlapPropertyChanged)));
        internal TabControl TabControl;

        // Events
        internal event EventHandler VisibleItemsChanged;

        // Methods
        public TabPanel()
        {
            SetCustomDefaultValues();
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            this.DefaultMeasureStartRank();
            ArrangementInfo info = new ArrangementInfo(base.Children.Count);
            if ((finalSize.Width > 0.0) && (finalSize.Height > 0.0))
            {
                double safeOverlap = this.CalcSafeOverlap();
                bool flag = true;
                while (flag)
                {
                    flag = false;
                    info = this.CalculatePlaces(finalSize, safeOverlap);
                    if ((info.ArrangedItems < this.TabControl.Ranking.RankedCount) && (this.StartRank > 0))
                    {
                        int num2 = this.TabControl.Ranking.IndexFromRank((this.StartRank + this.TabControl.PinnedItemsCount) - 1);
                        Size desiredSize = new Size();
                        if (num2 != -1)
                        {
                            desiredSize = base.Children[num2].DesiredSize;
                        }
                        if (((this._startRank + info.ArrangedItems) == this.TabControl.Ranking.RankedCount) && (((this.Orientation == Orientation.Horizontal) && (finalSize.Width >= ((info.UsedWidth + desiredSize.Width) - safeOverlap))) || ((this.Orientation == Orientation.Vertical) && (finalSize.Height >= ((info.UsedHeight + desiredSize.Height) - safeOverlap)))))
                        {
                            this._startRank--;
                            if (this._startRank != -1)
                            {
                                int num3 = this.TabControl.Ranking.IndexFromRank(this._startRank);
                                (base.Children[num3] as TabItem).RootIsVisible = true;
                            }
                            flag = true;
                        }
                    }
                }
            }
            Dictionary<double, object> dictionary = new Dictionary<double, object>();
            for (int i = 0; i < base.Children.Count; i++)
            {
                int index = this.TabControl.Ranking.RankFromIndex(i);
                TabItem item = base.Children[i] as TabItem;
                if (item.RootIsVisible)
                {
                    if (index == -1)
                    {
                        Rect finalRect = new Rect();
                        item.Arrange(finalRect);
                    }
                    else
                    {
                        item.Arrange(info.Places[index]);
                        if ((info.Places[index].Width > 0.0) && (info.Places[index].Height > 0.0))
                        {
                            if (this.Orientation == Orientation.Horizontal)
                            {
                                if (!dictionary.ContainsKey(info.Places[index].Left))
                                {
                                    dictionary.Add(info.Places[index].Left, this.TabControl.Items[i]);
                                }
                            }
                            else if (!dictionary.ContainsKey(info.Places[index].Top))
                            {
                                dictionary.Add(info.Places[index].Top, this.TabControl.Items[i]);
                            }
                        }
                    }
                }
            }
            this.SetZIndex();
            if (this.VisibleItemsCount != info.ArrangedItems)
            {
                this.VisibleItemsCount = info.ArrangedItems;
                this.OnVisibleItemsChanged();
            }
            this.TabControl.ChangeVisibleItems((from x in dictionary
                                                orderby x.Key
                                                select x.Value).ToList<object>());
            return new Size(Math.Max(0.0, info.UsedWidth), Math.Max(0.0, info.UsedHeight));
        }

        private double CalcSafeOverlap()
        {
            double num = Math.Max(0, this.Overlap);
            foreach (UIElement element in base.Children)
            {
                TabItem item = element as TabItem;
                if (((item == null) || item.RootIsVisible) && (element.Visibility == Visibility.Visible))
                {
                    if (this.Orientation == Orientation.Horizontal)
                    {
                        num = Math.Min(num, element.DesiredSize.Width / 2.0);
                    }
                    else
                    {
                        num = Math.Min(num, element.DesiredSize.Height / 2.0);
                    }
                }
            }
            return num;
        }

        private ArrangementInfo CalculatePlaces(Size finalSize, double safeOverlap)
        {
            ArrangementInfo info = new ArrangementInfo(base.Children.Count);
            Point nextLocation = new Point();
            bool flag = false;
            for (int i = 0; i < this.TabControl.PinnedItemsCount; i++)
            {
                int num2 = this.TabControl.Ranking.IndexFromRank(i);
                if (num2 == -1)
                {
                    info.ArrangedItems++;
                }
                else
                {
                    TabItem item = base.Children[num2] as TabItem;
                    Rect rect = this.GetRect(nextLocation, item);
                    if (item.Visibility == Visibility.Visible)
                    {
                        if (this.Orientation == Orientation.Horizontal)
                        {
                            nextLocation.X += item.DesiredSize.Width - safeOverlap;
                        }
                        else
                        {
                            nextLocation.Y += item.DesiredSize.Height - safeOverlap;
                        }
                    }
                    if ((!item.RootIsVisible || ((this.Orientation == Orientation.Horizontal) && ((nextLocation.X + safeOverlap) > finalSize.Width))) || ((this.Orientation == Orientation.Vertical) && ((nextLocation.Y + safeOverlap) > finalSize.Height)))
                    {
                        flag = true;
                        break;
                    }
                    info.Places[i] = rect;
                    info.ArrangedItems++;
                    if (item.Visibility == Visibility.Visible)
                    {
                        if (this.Orientation == Orientation.Horizontal)
                        {
                            info.UsedWidth = nextLocation.X + safeOverlap;
                            info.UsedHeight = Math.Max(info.UsedHeight, item.DesiredSize.Height);
                        }
                        else
                        {
                            info.UsedWidth = Math.Max(info.UsedWidth, item.DesiredSize.Width);
                            info.UsedHeight = nextLocation.Y + safeOverlap;
                        }
                    }
                }
            }
            for (int j = this.StartRank + this.TabControl.PinnedItemsCount; j < this.TabControl.Ranking.RankedCount; j++)
            {
                if (flag)
                {
                    flag = false;
                    break;
                }
                int num4 = this.TabControl.Ranking.IndexFromRank(j);
                if (num4 == -1)
                {
                    info.ArrangedItems++;
                }
                else
                {
                    TabItem item2 = base.Children[num4] as TabItem;
                    Rect rect2 = this.GetRect(nextLocation, item2);
                    if (item2.Visibility == Visibility.Visible)
                    {
                        if (this.Orientation == Orientation.Horizontal)
                        {
                            nextLocation.X += item2.DesiredSize.Width - safeOverlap;
                        }
                        else
                        {
                            nextLocation.Y += item2.DesiredSize.Height - safeOverlap;
                        }
                    }
                    if ((!item2.RootIsVisible || ((this.Orientation == Orientation.Horizontal) && ((nextLocation.X + safeOverlap) > finalSize.Width))) || ((this.Orientation == Orientation.Vertical) && ((nextLocation.Y + safeOverlap) > finalSize.Height)))
                    {
                        break;
                    }
                    info.Places[j] = rect2;
                    info.ArrangedItems++;
                    if (item2.Visibility == Visibility.Visible)
                    {
                        if (this.Orientation == Orientation.Horizontal)
                        {
                            info.UsedWidth = nextLocation.X + safeOverlap;
                            info.UsedHeight = Math.Max(info.UsedHeight, item2.DesiredSize.Height);
                        }
                        else
                        {
                            info.UsedWidth = Math.Max(info.UsedWidth, item2.DesiredSize.Width);
                            info.UsedHeight = nextLocation.Y + safeOverlap;
                        }
                    }
                }
            }
            if ((info.UsedWidth < 0.0) || (info.UsedHeight < 0.0))
            {
                info.UsedWidth = Math.Max(0.0, info.UsedWidth);
                info.UsedHeight = Math.Max(0.0, info.UsedHeight);
            }
            return info;
        }

        private void DefaultMeasureStartRank()
        {
            this._startRank = this._measureStartRank;
        }

        private Rect GetRect(Point nextLocation, UIElement item)
        {
            FrameworkElement element = item as FrameworkElement;
            double height = item.DesiredSize.Height;
            double y = nextLocation.Y;
            double width = item.DesiredSize.Width;
            double x = nextLocation.X;
            if (element != null)
            {
                if (this.Orientation == Orientation.Horizontal)
                {
                    switch (element.VerticalAlignment)
                    {
                        case VerticalAlignment.Center:
                            y += (base.DesiredSize.Height - item.DesiredSize.Height) / 2.0;
                            break;

                        case VerticalAlignment.Bottom:
                            y += base.DesiredSize.Height - item.DesiredSize.Height;
                            break;

                        case VerticalAlignment.Stretch:
                            height = base.DesiredSize.Height;
                            break;
                    }
                }
                if (this.Orientation == Orientation.Vertical)
                {
                    switch (element.HorizontalAlignment)
                    {
                        case HorizontalAlignment.Center:
                            x += (base.DesiredSize.Width - item.DesiredSize.Width) / 2.0;
                            break;

                        case HorizontalAlignment.Right:
                            x += base.DesiredSize.Width - item.DesiredSize.Width;
                            break;

                        case HorizontalAlignment.Stretch:
                            width = base.DesiredSize.Width;
                            break;
                    }
                }
            }
            return new Rect(Math.Ceiling(x), Math.Ceiling(y), width, height);
        }

        private void MeasureItems(Size finalSize, double safeOverlap)
        {
            Point point = new Point();
            for (int i = 0; i < this.TabControl.PinnedItemsCount; i++)
            {
                int num2 = this.TabControl.Ranking.IndexFromRank(i);
                if (num2 != -1)
                {
                    TabItem item = base.Children[num2] as TabItem;
                    if (((this.Orientation == Orientation.Horizontal) && (point.X >= finalSize.Width)) || ((this.Orientation == Orientation.Vertical) && (point.Y >= finalSize.Height)))
                    {
                        item.RootIsVisible = false;
                    }
                    else
                    {
                        item.Measure(finalSize);
                        if (item.Visibility == Visibility.Visible)
                        {
                            if (this.Orientation == Orientation.Horizontal)
                            {
                                point.X += item.DesiredSize.Width - safeOverlap;
                            }
                            else
                            {
                                point.Y += item.DesiredSize.Height - safeOverlap;
                            }
                        }
                    }
                }
            }
            for (int j = this.StartRank + this.TabControl.PinnedItemsCount; j < this.TabControl.Ranking.RankedCount; j++)
            {
                int num4 = this.TabControl.Ranking.IndexFromRank(j);
                if (num4 != -1)
                {
                    TabItem item2 = base.Children[num4] as TabItem;
                    if (((this.Orientation == Orientation.Horizontal) && (point.X >= finalSize.Width)) || ((this.Orientation == Orientation.Vertical) && (point.Y >= finalSize.Height)))
                    {
                        item2.RootIsVisible = false;
                    }
                    else
                    {
                        item2.RootIsVisible = true;
                        item2.Measure(finalSize);
                        if (item2.Visibility == Visibility.Visible)
                        {
                            if (this.Orientation == Orientation.Horizontal)
                            {
                                point.X += item2.DesiredSize.Width - safeOverlap;
                            }
                            else
                            {
                                point.Y += item2.DesiredSize.Height - safeOverlap;
                            }
                        }
                    }
                }
            }
            if (((this.Orientation == Orientation.Horizontal) && (point.X <= (finalSize.Width - safeOverlap))) || ((this.Orientation == Orientation.Vertical) && (point.Y <= (finalSize.Height - safeOverlap))))
            {
                for (int k = this.StartRank - 1; k >= 0; k--)
                {
                    int num6 = this.TabControl.Ranking.IndexFromRank(k);
                    if (num6 != -1)
                    {
                        TabItem item3 = base.Children[num6] as TabItem;
                        if (((this.Orientation == Orientation.Horizontal) && (point.X >= finalSize.Width)) || ((this.Orientation == Orientation.Vertical) && (point.Y >= finalSize.Height)))
                        {
                            break;
                        }
                        item3.RootIsVisible = true;
                        item3.Measure(finalSize);
                        if (item3.Visibility == Visibility.Visible)
                        {
                            if (this.Orientation == Orientation.Horizontal)
                            {
                                point.X += item3.DesiredSize.Width - safeOverlap;
                            }
                            else
                            {
                                point.Y += item3.DesiredSize.Height - safeOverlap;
                            }
                        }
                    }
                }
            }
            if ((this.TabControl.SelectedIndex != -1) && (base.Children[this.TabControl.SelectedIndex] is TabItem))
            {
                (base.Children[this.TabControl.SelectedIndex] as TabItem).RootIsVisible = true;
                (base.Children[this.TabControl.SelectedIndex] as TabItem).Measure(finalSize);
            }
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            double num = 0.0;
            double num2 = 0.0;
            double num3 = 0.0;
            int num4 = 0;
            this.MeasureItems(availableSize, (double)this.Overlap);
            for (int i = 0; i < base.Children.Count; i++)
            {
                UIElement element = base.Children[i];
                if ((element as TabItem).RootIsVisible && (element.Visibility == Visibility.Visible))
                {
                    if (this.Orientation == Orientation.Horizontal)
                    {
                        num += element.DesiredSize.Width;
                        num2 = Math.Max(num2, element.DesiredSize.Height);
                    }
                    else
                    {
                        num = Math.Max(num, element.DesiredSize.Width);
                        num2 += element.DesiredSize.Height;
                    }
                    num4++;
                }
            }
            num3 = (num4 > 1) ? (this.CalcSafeOverlap() * (num4 - 1)) : 0.0;
            if (this.Orientation == Orientation.Horizontal)
            {
                return new Size(Math.Min(availableSize.Width, Math.Max((double)0.0, (double)(num - num3))), Math.Min(availableSize.Height, num2));
            }
            return new Size(Math.Min(availableSize.Width, num), Math.Min(availableSize.Height, Math.Max((double)0.0, (double)(num2 - num3))));
        }

        private static void OnOrientationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TabPanel panel = d as TabPanel;
            Orientation oldValue = (Orientation)e.OldValue;
            Orientation newValue = (Orientation)e.NewValue;
            if (newValue != oldValue)
            {
                panel.InvalidateMeasure();
            }
        }

        private static void OnOverlapDirectionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TabPanel panel = d as TabPanel;
            TabPanelOverlapDirection oldValue = (TabPanelOverlapDirection)e.OldValue;
            TabPanelOverlapDirection newValue = (TabPanelOverlapDirection)e.NewValue;
            if (newValue != oldValue)
            {
                panel.InvalidateMeasure();
            }
        }

        private static void OnOverlapPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TabPanel panel = d as TabPanel;
            int oldValue = (int)e.OldValue;
            int newValue = (int)e.NewValue;
            if (newValue != oldValue)
            {
                panel.InvalidateMeasure();
            }
        }

        internal void OnVisibleItemsChanged()
        {
            if (this.VisibleItemsChanged != null)
            {
                this.VisibleItemsChanged(this, EventArgs.Empty);
            }
        }

        private void SetCustomDefaultValues()
        {
            base.HorizontalAlignment = HorizontalAlignment.Left;
            base.VerticalAlignment = VerticalAlignment.Top;
        }

        private void SetZIndex()
        {
            if (base.Children.Count != 0)
            {
                int selectedIndex = this.TabControl.SelectedIndex;
                if (selectedIndex == -1)
                {
                    selectedIndex = 0;
                }
                int num2 = this.TabControl.Ranking.RankFromIndex(selectedIndex);
                switch (this.OverlapDirection)
                {
                    case TabPanelOverlapDirection.Right:
                        for (int i = 0; i < this.TabControl.Ranking.RankedCount; i++)
                        {
                            int num4 = this.TabControl.Ranking.IndexFromRank(i);
                            Canvas.SetZIndex(base.Children[num4], base.Children.Count - i);
                        }
                        break;

                    case TabPanelOverlapDirection.Left:
                        for (int j = 0; j < this.TabControl.Ranking.RankedCount; j++)
                        {
                            int num6 = this.TabControl.Ranking.IndexFromRank(j);
                            Canvas.SetZIndex(base.Children[num6], j);
                        }
                        break;

                    case TabPanelOverlapDirection.RightLeftFromSelected:
                        for (int k = 0; k < num2; k++)
                        {
                            int num8 = this.TabControl.Ranking.IndexFromRank(k);
                            Canvas.SetZIndex(base.Children[num8], (base.Children.Count - num2) + k);
                        }
                        for (int m = num2; m < this.TabControl.Ranking.RankedCount; m++)
                        {
                            int num10 = this.TabControl.Ranking.IndexFromRank(m);
                            Canvas.SetZIndex(base.Children[num10], (base.Children.Count - m) + num2);
                        }
                        break;
                }
                Canvas.SetZIndex(base.Children[selectedIndex], base.Children.Count);
            }
        }

        internal void SyncMeasureStartRank()
        {
            this._measureStartRank = this._startRank;
        }

        // Properties
        public Orientation Orientation
        {
            get { return (Orientation)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }

        internal int Overlap
        {
            get { return (int)GetValue(OverlapProperty); }
            set { SetValue(OverlapProperty, value); }
        }

        internal TabPanelOverlapDirection OverlapDirection
        {
            get { return (TabPanelOverlapDirection)GetValue(OverlapDirectionProperty); }
            set { SetValue(OverlapDirectionProperty, value); }
        }

        internal int StartRank
        {
            get { return this._startRank; }
            set
            {
                if (((this._startRank != value) && (value >= 0)) && (value < base.Children.Count))
                {
                    this._startRank = value;
                    this._measureStartRank = value;
                    base.InvalidateArrange();
                }
            }
        }

        internal List<object> VisibleItems { get; set; }
        internal int VisibleItemsCount { get; set; }

        // Nested Types
        private class ArrangementInfo
        {
            // Methods
            public ArrangementInfo(int qty)
            {
                this.Places = new Rect[qty];
            }

            // Properties
            public int ArrangedItems { get; set; }

            public Rect[] Places { get; private set; }

            public double UsedHeight { get; set; }

            public double UsedWidth { get; set; }
        }
    }
}
